<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>铃声管理系统</title>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <div class="container">
        <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
            <h1>铃声管理系统</h1>
            <a href="/logout" class="action-button">退出登录</a>
        </div>
        <!-- 播放控制区域 -->
        <div class="card">
            <div class="section-title">音频播放控制</div>
            <div class="form-group">
                <label for="playMp3File">选择音频文件 (请忽略由于网络不稳定导致的JSON错误):</label>
                <select id="playMp3File" class="input-field">
                    <option value="">请选择音频文件</option>
                    <!-- 音频文件选项将通过JavaScript动态加载 -->
                </select>
                <div class="button-group">
                    <button class="button" onclick="refreshAudioList()">刷新列表</button>
                </div>
            </div>

            <div class="form-group">
                <label>音量控制 (0% - 200%):</label>
                <div class="volume-control">
                    <!-- 音量滑块容器 -->
                    <div class="volume-container">
                        <div class="volume-fill" id="volumeFill"></div>
                        <input type="range" id="volume" min="0" max="200" value="80" class="volume-slider">
                    </div>
                    <span id="volumeValue" class="volume-value">80</span>%
                </div>
                <div class="button-group">
                    <button class="button secondary" onclick="playBell()">播放选中音频</button>
                    <button class="button danger" onclick="stopBell()">紧急停止播放</button>
                </div>
            </div>
        </div>

        <!-- 上传区域 -->
        <div class="card">
            <div class="section-title">音乐上传</div>
            <div class="form-group">
                <label for="musicFile">选择本地音频文件 (支持.mp3, .wav, .ogg):</label>
                <input type="file" id="musicFile" accept=".mp3,.wav,.ogg" class="input-field">
                <div class="button-group">
                    <button class="button" onclick="uploadMusic()">上传音乐</button>
                </div>
                <br>
                <span id="uploadStatus" class="status-message"></span>
            </div>
        </div>

        <!-- 录音区域 -->
        <div class="card">
            <div class="section-title">录音功能</div>
            <div class="form-group">
                <p style="margin-bottom: 10px; color: #777; font-size: 14px;">
                    过长录音建议录好后上传，避免因网络不稳定导致的失败!
                </p>
                <div class="button-group">
                    <button class="button danger" id="recordButton" onclick="toggleRecording()">开始录音</button>
                </div>
                <br>
                <span id="recordingStatus" class="status-message"></span>
            </div>
        </div>

        <!-- 铃声时间表 -->
        <div class="card">
            <div class="section-title">铃声时间表</div>
            <div class="config-path">配置文件路径: <span id="configPath">daily_configs/</span></div>
            <table id="scheduleTable">
                <thead>
                    <tr>
                        <th>时间</th>
                        <th>音频文件</th>
                    </tr>
                </thead>
                <tbody>
                    <!-- 铃声安排将在这里显示 -->
                </tbody>
            </table>
        </div>
    </div>

    <script>
        function fetchConfigPath() {
            fetch('/get_config_path')
                .then(response => response.json())
                .then(data => {
                    document.getElementById('configPath').textContent = data.config_path;
                })
                .catch(error => console.error('Error fetching config path:', error));
        }

        function fetchSchedules() {
            fetch('/get_schedules')
                .then(response => response.json())
                .then(data => {
                    const tableBody = document.getElementById('scheduleTable').getElementsByTagName('tbody')[0];
                    tableBody.innerHTML = '';
                    
                    for (const time in data) {
                        const row = tableBody.insertRow();
                        const cellTime = row.insertCell(0);
                        const cellMp3 = row.insertCell(1);
                        cellTime.textContent = time;
                        cellMp3.textContent = data[time];
                    }

                    // 如果没有数据，显示提示信息
                    if (tableBody.rows.length === 0) {
                        const row = tableBody.insertRow();
                        const cell = row.insertCell(0);
                        cell.colSpan = 2;
                        cell.textContent = '暂无铃声安排';
                        cell.style.textAlign = 'center';
                        cell.style.padding = '20px';
                        cell.style.color = '#777';
                    }
                })
                .catch(error => console.error('Error fetching schedules:', error));
        }

        function updateSchedule() {
            const time = document.getElementById('time').value;
            const mp3File = document.getElementById('mp3File').value;

            fetch('/update_schedule', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ time: time, mp3_file: mp3File })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    alert(data.success);
                    fetchSchedules();
                } else {
                    alert(data.error);
                }
            })
            .catch(error => {
                alert('更新失败: ' + error);
            });
        }

        function playBell() {
            const select = document.getElementById('playMp3File');
            const mp3File = select.options[select.selectedIndex].value;
            const volume = document.getElementById('volume').value;

            if (!mp3File) {
                alert('请先选择一个音频文件');
                return;
            }

            fetch('/play_bell', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ mp3_file: mp3File, volume: volume })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    alert(data.success);
                } else {
                    alert(data.error);
                }
            })
            .catch(error => {
                alert('播放失败: ' + error);
            });
        }

        function stopBell() {
            fetch('/stop_bell', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                }
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    alert(data.success);
                } else if (data.message) {
                    alert(data.message);
                } else {
                    alert(data.error);
                }
            })
            .catch(error => {
                alert('停止播放请求失败: ' + error);
            });
        }

        // 添加音量滑块事件监听 - 包含填充效果更新
        const volumeSlider = document.getElementById('volume');
        const volumeFill = document.getElementById('volumeFill');
        const volumeValue = document.getElementById('volumeValue');
        
        // 初始化填充宽度 (基于0-200范围)
        volumeFill.style.width = (volumeSlider.value / 2) + '%';
        
        // 监听滑块变化更新填充效果
        volumeSlider.addEventListener('input', function() {
            const value = this.value;
            volumeValue.textContent = value;
            volumeFill.style.width = (value / 2) + '%';
        });

        // 获取音频文件列表
        function refreshAudioList() {
            fetch('/get_audio_files')
                .then(response => response.json())
                .then(data => {
                    const select = document.getElementById('playMp3File');
                    select.innerHTML = '<option value="">请选择音频文件</option>';
                     

                    // 添加运行目录下的MP3文件
                    if (data.root_mp3 && data.root_mp3.length > 0) {
                        const optGroup = document.createElement('optgroup');
                        optGroup.label = '运行目录';
                        data.root_mp3.forEach(file => {
                            const option = document.createElement('option');
                            option.value = file;
                            option.textContent = file;
                            optGroup.appendChild(option);
                        });
                        select.appendChild(optGroup);
                    }
                     

                    // 添加voice文件夹下的音频文件 (倒序)
                    if (data.voice_files && data.voice_files.length > 0) {
                        const optGroup = document.createElement('optgroup');
                        optGroup.label = '麦克风录制 (voice)';
                        // 反转数组实现倒序显示
                        data.voice_files.slice().reverse().forEach(file => {
                            const option = document.createElement('option');
                            option.value = 'voice/' + file;
                            option.textContent = file;
                            optGroup.appendChild(option);
                        });
                        select.appendChild(optGroup);
                    }
                     

                    // 添加music文件夹下的音频文件 (倒序)
                    if (data.music_files && data.music_files.length > 0) {
                        const optGroup = document.createElement('optgroup');
                        optGroup.label = '音乐文件 (music)';
                        // 反转数组实现倒序显示
                        data.music_files.slice().reverse().forEach(file => {
                            const option = document.createElement('option');
                            option.value = 'music/' + file;
                            option.textContent = file;
                            optGroup.appendChild(option);
                        });
                        select.appendChild(optGroup);
                    }
                })
                .catch(error => console.error('Error fetching audio files:', error));
        }
        

        // 录音功能
        let audioContext;
        let analyser;
        let microphone;
        let scriptProcessor;
        let isRecording = false;
        let audioData = [];
        let sampleRate = 44100;
        let channels = 1;
        let bitsPerSample = 16;

        // 初始化音频上下文和处理器
        function initAudioProcessing(stream) {
            audioContext = new (window.AudioContext || window.webkitAudioContext)({
                sampleRate: sampleRate
            });
            analyser = audioContext.createAnalyser();
            analyser.fftSize = 2048;

            microphone = audioContext.createMediaStreamSource(stream);

            // 创建脚本处理器节点来获取原始音频数据
            scriptProcessor = audioContext.createScriptProcessor(4096, 1, 1);

            // 连接音频节点
            microphone.connect(analyser);
            analyser.connect(scriptProcessor);
            scriptProcessor.connect(audioContext.destination);

            // 处理音频数据
            scriptProcessor.onaudioprocess = function(e) {
                if (isRecording) {
                    const inputData = e.inputBuffer.getChannelData(0);
                    // 转换为16位PCM
                    const buffer = new ArrayBuffer(inputData.length * 2);
                    const view = new DataView(buffer);
                    for (let i = 0; i < inputData.length; i++) {
                        // 裁剪并转换为16位整数
                        const sample = Math.max(-1, Math.min(1, inputData[i]));
                        const int16Sample = sample < 0 ? sample * 0x8000 : sample * 0x7FFF;
                        view.setInt16(i * 2, int16Sample, true);
                        audioData.push(int16Sample);
                    }
                }
            };
        }

        // 停止音频处理
        function stopAudioProcessing() {
            if (scriptProcessor) {
                scriptProcessor.disconnect();
                scriptProcessor = null;
            }
            if (audioContext) {
                audioContext.close();
                audioContext = null;
            }
        }

        // 构建WAV文件头
        function buildWaveHeader(sampleRate, channels, bitsPerSample, dataLength) {
            const header = new ArrayBuffer(44);
            const view = new DataView(header);

            // RIFF标志
            writeString(view, 0, 'RIFF');
            // 文件大小 = 44 + 数据长度
            view.setUint32(4, 44 + dataLength, true);
            // WAVE标志
            writeString(view, 8, 'WAVE');
            // fmt子块
            writeString(view, 12, 'fmt ');
            // fmt子块大小
            view.setUint32(16, 16, true);
            // 格式类型(PCM)
            view.setUint16(20, 1, true);
            // 声道数
            view.setUint16(22, channels, true);
            // 采样率
            view.setUint32(24, sampleRate, true);
            // 字节率 = 采样率 * 声道数 * 位数/8
            view.setUint32(28, sampleRate * channels * (bitsPerSample / 8), true);
            // 块对齐 = 声道数 * 位数/8
            view.setUint16(32, channels * (bitsPerSample / 8), true);
            // 位数
            view.setUint16(34, bitsPerSample, true);
            // data子块
            writeString(view, 36, 'data');
            // 数据长度
            view.setUint32(40, dataLength, true);

            return header;
        }

        // 写入字符串到DataView
        function writeString(view, offset, string) {
            for (let i = 0; i < string.length; i++) {
                view.setUint8(offset + i, string.charCodeAt(i));
            }
        }

        function toggleRecording() {
            if (isRecording) {
                stopRecording();
            } else {
                startRecording();
            }
        }

        function startRecording() {
            if (isRecording) return;

            // 配置高质量音频选项
            const audioConstraints = {
                audio: {
                    sampleRate: sampleRate,
                    channelCount: channels,
                    sampleSize: bitsPerSample,
                    echoCancellation: true,
                    noiseSuppression: true,
                    autoGainControl: true
                }
            };

            navigator.mediaDevices.getUserMedia(audioConstraints)
                .then(stream => {
                    // 初始化音频处理
                    initAudioProcessing(stream);

                    // 开始录音
                    isRecording = true;
                    audioData = []; // 清空之前的录音数据
                    document.getElementById('recordingStatus').textContent = '正在录音...';
                    document.getElementById('recordingStatus').className = 'status-message error';
                    document.getElementById('recordButton').textContent = '停止录音';
                })
                .catch(error => {
                    alert('无法访问麦克风: ' + error);
                });
        }

        function stopRecording() {
            if (!isRecording) return;

            isRecording = false;
            document.getElementById('recordingStatus').textContent = '录音已停止，正在尝试上传';
            document.getElementById('recordingStatus').className = 'status-message info';
            document.getElementById('recordButton').textContent = '开始录音';
            stopAudioProcessing();

            // 构建WAV文件
            const dataLength = audioData.length * 2; // 每个样本2字节
            const waveHeader = buildWaveHeader(sampleRate, channels, bitsPerSample, dataLength);

            // 创建完整的WAV文件数据
            const waveData = new Uint8Array(waveHeader.byteLength + dataLength);
            waveData.set(new Uint8Array(waveHeader), 0);

            // 写入音频数据
            for (let i = 0; i < audioData.length; i++) {
                waveData.set([
                    audioData[i] & 0xFF, // 低字节
                    (audioData[i] >> 8) & 0xFF // 高字节
                ], waveHeader.byteLength + i * 2);
            }

            // 创建Blob
            const audioBlob = new Blob([waveData], { type: 'audio/wav' });

            // 准备FormData上传，包含音频参数
            const formData = new FormData();
            formData.append('audio', audioBlob, 'recording.wav');
            formData.append('sample_rate', sampleRate);
            formData.append('channels', channels);
            formData.append('bits_per_sample', bitsPerSample);

            fetch('/upload_recording', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    document.getElementById('recordingStatus').textContent = '录音上传成功!';
                    document.getElementById('recordingStatus').className = 'status-message success';
                    alert('录音成功! 文件已保存为: ' + data.filename);
                    refreshAudioList(); // 上传成功后刷新音频列表
                } else {
                    document.getElementById('recordingStatus').textContent = '录音上传失败: ' + data.error;
                    document.getElementById('recordingStatus').className = 'status-message error';
                }
                setTimeout(() => {
                    document.getElementById('recordingStatus').textContent = '';
                    document.getElementById('recordingStatus').className = '';
                }, 3000);
            })
            .catch(error => {
                document.getElementById('recordingStatus').textContent = '上传失败: ' + error;
                document.getElementById('recordingStatus').className = 'status-message error';
                setTimeout(() => {
                    document.getElementById('recordingStatus').textContent = '';
                    document.getElementById('recordingStatus').className = '';
                }, 3000);
            });
        }

        // 音乐上传功能
        function uploadMusic() {
            const musicFileInput = document.getElementById('musicFile');
            const uploadStatus = document.getElementById('uploadStatus');

            if (!musicFileInput.files || musicFileInput.files.length === 0) {
                uploadStatus.textContent = '请选择音乐文件';
                uploadStatus.className = 'status-message info';
                setTimeout(() => { 
                    uploadStatus.textContent = ''; 
                    uploadStatus.className = '';
                }, 3000);
                return;
            }

            const musicFile = musicFileInput.files[0];
            uploadStatus.textContent = '正在上传...';
            uploadStatus.className = 'status-message info';

            // 准备FormData上传
            const formData = new FormData();
            formData.append('music', musicFile);

            fetch('/upload_music', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    uploadStatus.textContent = '上传成功!';
                    uploadStatus.className = 'status-message success';
                    alert('音乐上传成功! 文件已保存为: ' + data.filename);
                    refreshAudioList(); // 上传成功后刷新音频列表
                    musicFileInput.value = ''; // 清空文件选择
                } else {
                    uploadStatus.textContent = '上传失败: ' + data.error;
                    uploadStatus.className = 'status-message error';
                }
                // 3秒后清除状态消息
                setTimeout(() => { 
                    uploadStatus.textContent = ''; 
                    uploadStatus.className = '';
                }, 3000);
            })
            .catch(error => {
                uploadStatus.textContent = '上传失败: ' + error;
                uploadStatus.className = 'status-message error';
                setTimeout(() => { 
                    uploadStatus.textContent = ''; 
                    uploadStatus.className = '';
                }, 3000);
            });
        }
        

        // 页面加载时获取铃声安排和音频文件列表
        window.onload = function() {
            fetchSchedules();
            refreshAudioList();
            fetchConfigPath();
        };
    </script>
</body>
</html>
